#include "meditor.h"

#include <QKeyEvent>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QFileInfo>
#include <QTextStream>
#include <QTextCodec>
#include <QInputDialog>
#include <QPrintDialog>
#include <QDir>
#include <QDateTime>
#include <QMessageBox>
#include <QColor>
#include <QCoreApplication>
#include <QPixmap>
#include <QMenu>
#include <QContextMenuEvent>

#include "mannasettings.h"
#include "fileoptiondlg.h"
#include "editortabwidget.h"
#include "mainwindow.h"
#include "helpwidget.h"

using namespace optMannaSettings;

bool MEditor::m_pasteAvailableInit = false;
bool MEditor::m_pasteAvailable = false;

MEditor::MEditor( EditorTabWidget* p )
    : QsciScintilla( p )
{
    // deal with utf8
    setUtf8(true);

    m_editorTab = static_cast<EditorTabWidget*> (p);
    MainWindow *mw = static_cast<MainWindow*> (p->parent());
    m_lastModified = 0;

    // Search record
    m_findExpr = "";
    m_regExpr = false;
    m_caseSensitive = false;
    m_wholeWord = false;
    m_findForward = true;

    // connection
    connect( this, SIGNAL( linesChanged() ), this, SLOT( linesChanged() ) );
    connect( this, SIGNAL( copyAvailable( bool ) ), this, SLOT( setCopyAvailable( bool ) ) );
    connect( this, SIGNAL( cursorPositionChanged( int, int ) ), this, SLOT( cursorPositionChanged( int, int ) ) );
    connect( this, SIGNAL( textChanged() ), this, SLOT( textChanged() ) );
    connect( this, SIGNAL(modificationChanged (bool) ), this, SLOT( slotEditorModified (bool)));
    connect( QApplication::clipboard(), SIGNAL( dataChanged() ), this, SLOT( clipboardDataChanged() ) );

    // init pasteAvailable
    if ( !m_pasteAvailableInit ) {
        m_pasteAvailableInit = true;
        m_pasteAvailable = !QApplication::clipboard()->text().isEmpty();
    }
    setCaretLineBackgroundColor (QColor(170,240,255));
    setCaretLineVisible (false);

#if 0
    SendScintilla( QsciScintillaBase::SCI_CLEARALLCMDKEYS );
    SendScintilla( QsciScintillaBase::SCI_ASSIGNCMDKEY, SCK_TAB, SCI_TAB);
    SendScintilla( QsciScintillaBase::SCI_ASSIGNCMDKEY, SCK_ESCAPE, SCI_CANCEL);
    SendScintilla( QsciScintillaBase::SCI_ASSIGNCMDKEY, SCK_RETURN, SCI_NEWLINE);

    // By default control characters don't do anything (rather than insert the
    // control character into the text). (c) Phil
    for (int k = 'A'; k <= 'Z'; ++k)
        SendScintilla(QsciScintillaBase::SCI_ASSIGNCMDKEY,
            k + (QsciScintillaBase::SCMOD_CTRL << 16),
            QsciScintillaBase::SCI_NULL);
#endif
    m_modified = false;

    m_breakMarker    = markerDefine(QPixmap(":/images/edit/breakpoint_tiny.png"));
    m_condBkptMarker= markerDefine(QPixmap(":/images/edit/breakpoint_tiny_cond.png"));
    m_stepMarker    = markerDefine(QPixmap(":/images/debug/next.png"));
    m_stepHandle    = -1;

    setMarginSensitivity(SC_MARGIN_NUMBER, true);
    connect (this, SIGNAL(marginClicked(int,int,Qt::KeyboardModifiers)),
             this, SLOT(clickToggleBreakpoint(int,int,Qt::KeyboardModifiers)));
    connect (this, SIGNAL(bkptToggleByClicked(QString,int)),
             mw, SLOT(slotToggleBreakpoint(QString, int)));
    connect (this, SIGNAL(condBkptAdded(QString)),
             mw, SLOT(slotToggleBreakpoint(QString)));

    setMouseTracking(true);
    //magic number. 1 is the number of the margin breakpoint, 8 is the number of the hand cursor
    SendScintilla(SCI_SETMARGINCURSORN, 1, 8);

    SendScintilla(SCI_SETSCROLLWIDTH, 1);
    SendScintilla(SCI_SETSCROLLWIDTHTRACKING, true);
}

void MEditor::keyPressEvent( QKeyEvent* e )
{
    if ( !e->isAutoRepeat() && e->modifiers() & Qt::ControlModifier && e->key() == Qt::Key_Space ) {
        switch ( autoCompletionSource() ) {
            case QsciScintilla::AcsAll:
                autoCompleteFromAll();
            break;
            case QsciScintilla::AcsAPIs:
                autoCompleteFromAPIs();
            break;
            case QsciScintilla::AcsDocument:
                autoCompleteFromDocument();
            break;
            default:
            break;
        }
        return;
    }
    if (e->key() == Qt::Key_F1 && !helpHtml.isEmpty()) {
        QString url = "file:///" + helpHtml;
        HelpWidget::instance()->slotSetSource(url);
        HelpWidget::instance()->showMaximized();
    }
    else {
        beginUndoAction ();
        QsciScintilla::keyPressEvent( e );
        endUndoAction();
    }
}

void MEditor::mouseMoveEvent(QMouseEvent *event) {
    QsciScintilla::mouseMoveEvent(event);

    bool isId = false;
    long chpos = SendScintilla(SCI_POSITIONFROMPOINTCLOSE, event->x(), event->y());
    QString word = myGetWord(chpos);
    if (word.length() > 0) {
        isId = true;
        if (word[0] != '_' && !word[0].isLetter()) {
            isId = false;
        }

        for (int i = 1;i < word.length() && isId;++i) {
            if (word[i] != '_' && !word[i].isDigit() && !word[i].isLetter()) {
                isId = false;
                break;
            }
        }
    }
    if (isId) {
        showTooltip(word);
    } else {
        setToolTip("");
    }
}

void MEditor::wheelEvent(QWheelEvent *e)
{
    if (QApplication::keyboardModifiers() != Qt::ControlModifier) {
        QsciScintilla::wheelEvent(e);
        return;
    }
    m_editorTab->toZoom(SendScintilla(SCI_GETZOOM)+e->delta()/120);
}

bool MEditor::lineNumbersMarginEnabled() const
{
    return marginLineNumbers( 0 );
}

int MEditor::lineNumbersMarginWidth() const
{
    return property( "LineNumbersMarginWidth" ).toInt();
}

bool MEditor::lineNumbersMarginAutoWidth() const
{
    return property( "LineNumbersMarginAutoWidth" ).toBool();
}

void MEditor::setLineNumbersMarginEnabled( bool b )
{
    setMarginLineNumbers( 0, b );
}

void MEditor::setLineNumbersMarginWidth( int i )
{
    int j = i;
    if ( i != 0 )
        j++;

    setProperty( "LineNumbersMarginWidth", i );
    setMarginWidth( 0, QString().fill( '0', j ) );
}

void MEditor::setLineNumbersMarginAutoWidth( bool b )
{
    setProperty( "LineNumbersMarginAutoWidth", b );
    emit linesChanged();
}

void MEditor::linesChanged()
{
    if ( lineNumbersMarginAutoWidth() )
        setLineNumbersMarginWidth( QString::number( lines() ).length() );
}

bool MEditor::copyAvailable()
{
    return m_copyAvailable;
}

bool MEditor::canPaste()
{
    return m_pasteAvailable;
}

QPoint MEditor::cursorPosition() const
{
    return m_cursorPosition;
}

void MEditor::setCopyAvailable( bool b )
{
    m_copyAvailable = b;
}

void MEditor::cursorPositionChanged( int l, int p )
{
    m_cursorPosition = QPoint( p, l );
    emit cursorPositionChanged( m_cursorPosition );
}

void MEditor::textChanged()
{
    m_lastModified = QDateTime::currentDateTime ().toTime_t ();

    emit undoAvailable( isUndoAvailable() );
    emit redoAvailable( isRedoAvailable() );
}

void MEditor::clipboardDataChanged()
{
    m_pasteAvailable = !QApplication::clipboard()->text().isEmpty();
    emit pasteAvailable( canPaste() );
}

bool MEditor::openFile( const QString& s )
{
    if ( isModified() )
        return false;

    // open file
    QFile f( s );
    if ( !f.open( QFile::ReadOnly ) ) {
        QMessageBox::warning(this, tr( "Open file..." ),
                            tr( "Cannot read file %1:\n%2." ).arg( s ).arg( f.errorString() ) );
        return false;
    }

    // remember filename
    setProperty( "fileName", s );
    if (s.endsWith(".pas")) {
        pasSrc = true;
    } else {
        pasSrc = false;
    }
    qDebug("open %s", s.toUtf8().constData());

    // set lexer and apis
    QsciLexer* lexer = NULL;
    lexer = MannaSettings::getMannaSettings()->lexerForFileName(s);
    setLexer(lexer);
    setEditorProperties();
    // load file
    QApplication::setOverrideCursor( Qt::WaitCursor );
    QTextStream i( &f );
    if ( i.codec()->name() != qPrintable( defaultEncoding() ) )
        i.setCodec( qPrintable( defaultEncoding() ) );
    setText( i.readAll() );
    setModified( false );
    QApplication::restoreOverrideCursor();

    if (convertTabsUponOpen() )
        convertTabs();

    // make backup if needed
    if ( createBackupUponOpen() )
        makeBackup();

    // convert eol
    if (autoEolConversion() )
        convertEols( eolMode() );

    m_options.load(s);

    for (QMap<int, QString>::iterator i = m_options.m_breakPts.begin();
        i != m_options.m_breakPts.end(); ++i) {
        int line = i.key();
        doToggleBreakpont(line, i.value());
    }
    return true;
}

bool MEditor::saveFile( const QString& s )
{
    // get filename
    QString fn = property( "fileName" ).toString();
    if (fn.isEmpty() && s.isEmpty()) {
        ///* There must be something wrong. */
        //return false;
        fn = QFileDialog::getSaveFileName(this);
        if (fn.isEmpty())
            return false;
    }
    bool newFilename = ((fn.isEmpty() || fn != s) && !s.isEmpty());
    if ( !newFilename && !isModified()) {
            return true;
    }
    if (newFilename)
        fn = s;
    // get path
    QString fp = QFileInfo( fn ).path();

    // filename
    QFile f( fn );
    // filename dir
    QDir d;
    // create bak folder
    if ( !d.exists( fp ) )
        if ( !d.mkpath( fp ) )
            return false;

    // set correct path
    d.setPath( fp );
    // try open file to write in
    if ( !f.open( QFile::WriteOnly ) ) {
        QMessageBox::warning(this, tr( "Save file..." ),
                            tr( "Cannot write file %1:\n%2." ).arg( fn ).arg( f.errorString() ));
        return false;
    }

    // writing file
    QApplication::setOverrideCursor( Qt::WaitCursor );
    QTextStream o( &f );
    if ( o.codec()->name() != qPrintable( defaultEncoding() ) )
        o.setCodec( qPrintable( defaultEncoding() ) );
    o << text();
    setModified( false );
    QApplication::restoreOverrideCursor();

    // remember filename
    setProperty( "fileName", fn );

    f.close();
    if (newFilename) {
        closeFile();
        openFile(fn);
    }
    return true;
}

bool MEditor::saveBackup( const QString& s )
{
    // if not filename, cancel
    if ( s.isEmpty() )
        return false;

    // get path
    QString fp = QFileInfo( s ).path();

    // file
    QFile f( s );

    // filename dir
    QDir d;
    // create bak folder
    if ( !d.exists( fp ) )
        if ( !d.mkpath( fp ) )
            return false;

    // set correct path
    d.setPath( fp );

    // try open file to write in
    if ( !f.open( QFile::WriteOnly ) ) {
        QMessageBox::warning( this, tr( "Save backup..." ),
                            tr( "Cannot write file %1:\n%2." ).arg( s ).arg( f.errorString() ));
        return false;
    }

    // writing file
    QApplication::setOverrideCursor( Qt::WaitCursor );
    QTextStream o( &f );
    o << text();
    QApplication::restoreOverrideCursor();

    return true;
}

bool MEditor::closeFile(bool discard /* = false */)
{
    if (m_modified && !discard) {
        int ret = QMessageBox::warning(this, tr("File has been modified..."),
                    tr("The file %1 has been modified.\n"
                        "Do you want to save your changes?")
                        .arg(property( "fileName" ).toString()),
                    QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
                    QMessageBox::Save);
        if (ret == QMessageBox::Cancel) {
            return false;
        }
        if (ret == QMessageBox::Save) {
            if (!saveFile()) return false;
        }
    }

    QString fn = property( "fileName" ).toString();
    if (!fn.isEmpty()) {
        m_options.m_breakPts.clear();
        for (QMap<int, QString>::iterator i = m_bkpts.begin(); i != m_bkpts.end(); i++) {
            int handle = i.key();
            int line = markerLine(handle);
            if (handle != -1 && line != -1)
                m_options.m_breakPts.insert(line + 1, i.value());
        }
        m_options.save(fn);
    }
    clear();
    setModified( false );
    // clear filename
    setProperty( "fileName", QVariant() );

    return true;
}

void MEditor::print( bool b )
{
    // get printer
    QsciPrinter p;
    // set wrapmode
    p.setWrapMode( WrapWord );
    // if quick print
    if ( b ) {
        // check if default printer is set
        if ( p.printerName().isEmpty() ) {
            QMessageBox::warning( this, tr( "Quick Print..." ),
                                tr( "There is no defaullt printer, please set one before trying quick print" ));
            return;
        }
        // print and return
        p.printRange( this );
        return;
    }
    // printer dialog
    QPrintDialog d( &p );
    // if ok
    if ( d.exec() ) {
        // print
        int f = -1, t = -1, i;
        if ( d.printRange() == QPrintDialog::Selection )
        getSelection( &f, &i, &t, &i );
        p.printRange( this, f, t );
    }
}

void MEditor::quickPrint()
{
    print( true );
}

void MEditor::selectNone()
{
    selectAll( false );
}

void MEditor::invokeGoToLine()
{
    bool b;
    int l, i;
    getCursorPosition( &l, &i );
    int j = QInputDialog::getInt( this, tr( "Go To Line..." ),
                                    tr( "Enter the line number you want to go:" ), l +1, 1, lines(), 1, &b );
    if ( b ) {
        setCursorPosition( j -1, 0 );
        ensureLineVisible( j -1 );
        setFocus();
    }
}

bool MEditor::gotoBrace()
{
    moveToMatchingBrace();
    return true;
}

void MEditor::convertTabs( int i )
{
    int x, y;
    getCursorPosition( &y, &x );
    if ( i == -1 )
        i = tabWidth();
    bool b = findFirst( "\t", false, true, false, true, true );
    if ( b ) {
        QString r = QString().fill( ' ', i );
        replace( r );
        while ( findNext() )
        replace( r );
    }
    setCursorPosition( y, x );
}

void MEditor::makeBackup()
{
    // get filename
    const QString dn = ".bak";
    QFileInfo f( property( "fileName" ).toString() );
    const QString s = f.path().append( "/" ).append( dn ).append( "/" )
            .append( f.fileName() ).append( "." )
            .append( QDateTime::currentDateTime().toString( "yyyyMMdd_hhmmss" ) );

    // cancel if filename doesn't exists
    if ( !f.exists() )
        return;

    // filename dir
    QDir d( f.path() );

    // create bak folder
    if ( !d.exists( ".bak" ) )
        if ( !d.mkdir( ".bak" ) )
            return;

    QFile::copy( f.absoluteFilePath(), s );
}

void MEditor::gotoLine (int line, bool moveTop)
{
    int index, l;
    getCursorPosition (&l, &index);
    if (moveTop)
        index = 0;
    setCursorPosition( line -1, index);
    ensureLineVisible( line -1 );
    setFocus();
}

void MEditor::slotEditorModified(bool changed)
{
    m_modified = changed;
    emit editorModified(this, changed);
}

QString MEditor::getFileName() const
{
    return property( "fileName" ).toString();
}

bool MEditor::isNewFile()
{
    QString fn = property( "fileName" ).toString();

    return (fn.isEmpty() || fn.isNull());
}

void MEditor::setModified (bool m)
{
    if (!m) {
        QsciScintilla::setModified(m);
    } else {
        m_lastModified = QDateTime::currentDateTime ().toTime_t ();

        QsciScintilla::setModified(m);
        QKeyEvent *press = new QKeyEvent(QEvent::KeyPress, Qt::Key_Space, Qt::NoModifier, QString(" "));
        QKeyEvent *release = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Space, Qt::NoModifier, QString(" "));
        QCoreApplication::postEvent(this, press);
        QCoreApplication::postEvent(this, release);

        press = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier, QString("\b"));
        release = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier, QString("\b"));
        QCoreApplication::postEvent(this, press);
        QCoreApplication::postEvent(this, release);
    }
}

bool MEditor::toggleBreakpoint (int &line, bool cursor/* = false */, QString cond /* = QString()*/)
{
    int index, ln;
    if (cursor) {
        getCursorPosition (&ln, &index);
        line = ln + 1;
    }
    return doToggleBreakpont(line, cond);
}
//real line
//return true if added
bool MEditor::doToggleBreakpont(int line, QString cond)
{
    int l = line - 1;
    bool ret = false;
    unsigned mask = markersAtLine (l);
    bool bk = mask & (1 << m_breakMarker);
    bool condbk = mask & (1 << m_condBkptMarker);
    if (bk | condbk) {
        if (bk) markerDelete(l, m_breakMarker);
        else markerDelete(l, m_condBkptMarker);
        for (QMap<int, QString>::iterator i = m_bkpts.begin(); i != m_bkpts.end(); i++) {
            if (l == markerLine(i.key()) || -1 == markerLine(i.key())) {
                qDebug() << QString("MEditor: Remove bkpt at line %1, handle is %2")
                       .arg(l+1).arg(i.key());
                if (condbk) qDebug() << QString(", condition is %1").arg(i.value());
                m_bkpts.remove(i.key());
                break;
            }
        }
        ret = false;
    } else {
        int handle;
        if (cond.isEmpty()) {
            handle = markerAdd(l, m_breakMarker);
        } else {
            handle = markerAdd(l, m_condBkptMarker);
        }

        m_bkpts.insert(handle, cond);
        ret = true;
    }
    return ret;
}

void MEditor::getAllBreakpoints(QMap<int, QString> & brmap)
{
    brmap.clear();
    for (QMap<int, QString>::iterator i = m_bkpts.begin(); i != m_bkpts.end(); i++) {
        brmap.insert(markerLine(i.key()) + 1, i.value());
    }
    /*
    int line, newline;
    unsigned int mask = (1<<m_breakMarker);
    unsigned int condMask = (1<<m_condBkptMarker);
    newline = line = 0;
    while ((newline = markerFindNext(line, mask)) >= 0) {
        brmap.insert(newline + 1,QString(""));
        line = newline + 1;
    }
    newline = line = 0;
    while ((newline = markerFindNext(line, condMask)) >= 0) {
        brmap.insert(newline + 1,QString(""));
        line = newline + 1;
    }*/
}

void MEditor::search(QString expr, bool re, bool cs, bool whole, bool forward)
{
    if (m_findExpr == expr && m_regExpr == re && m_caseSensitive == cs && m_wholeWord == whole && !forward
            && m_findForward == forward) {
        //if (findNext())
        //    setFocus();
        searchPrev();
        return;
    }

    m_findExpr = expr;
    m_regExpr = re;
    m_caseSensitive = cs;
    m_wholeWord = whole;
    m_findForward = forward;


    if (selectedText().toLower() != expr.toLower()) {
        if (findFirst(expr, re, cs, whole, true,forward,-1,-1,true))
            setFocus();
    } else {
        if (findFirst(expr, re, cs, whole, true,forward,-1,-1,true))
            setFocus();
    }
}

void MEditor::searchNext()
{
    if (!m_findForward) {
        m_findForward = true;
        if (findFirst(m_findExpr, m_regExpr, m_caseSensitive, m_wholeWord, true, m_findForward))
            setFocus();
    } else {
        if (findNext())
            setFocus();
    }
}

void MEditor::searchPrev()
{
    if (m_findForward) {
        m_findForward = false;
        if (findFirst(m_findExpr, m_regExpr, m_caseSensitive, m_wholeWord, true, m_findForward))
            setFocus();
    } else {
        if (findNext()) {
            setFocus();
        }
    }
}

void MEditor::replaceCur(QString srcexpr,QString desexpr,bool re, bool cs, bool whole,bool replaceAll)
{
    if (!hasSelectedText() || selectedText() != srcexpr) {
        m_findExpr = srcexpr;
        m_regExpr = re;
        m_caseSensitive = cs;
        m_wholeWord = whole;
        m_findForward = true;
        if (findFirst(srcexpr, re, cs, whole, true,true,-1,-1,true))
            setFocus();
        else
            return;
    }

    int last_line,last_index,line,index,tmp1,tmp2;
    getSelection(&last_line,&last_index,&tmp1,&tmp2);
    if (replaceAll) {
        do {
            replace(desexpr);
            if (findNext()) {
                setFocus();
                getSelection(&line,&index,&tmp1,&tmp2);
                if ((last_line == line) && (last_index == index)) break;
            } else
                break;
        } while (1);
    } else {
        replace(desexpr);
        //if (findNext())
        //    setFocus();
    }
}

void MEditor::setEditorProperties()
{
    setMEditorProperties(this);
}

void MEditor::setFileOption()
{
    QString fname = QFileInfo(property( "fileName" ).toString()).fileName();

    if (fname.isEmpty())
        return;

    QString cmd;
#ifdef Q_OS_WIN
    if (fname.endsWith(".cpp", Qt::CaseInsensitive)) {
        cmd = QString("g++ -Wall -g -o %o %i");
    } else if (fname.endsWith(".c", Qt::CaseInsensitive)) {
        cmd = QString("gcc -Wall -g -o %o %i");
#else
    if (fname.endsWith(".cpp", Qt::CaseInsensitive)) {
        cmd = QString("g++ -Wall -g -o %o %i");
    } else if (fname.endsWith(".c", Qt::CaseInsensitive)) {
        cmd = QString("gcc -Wall -g -o %o %i");
#endif
    } else if (fname.endsWith(".pas", Qt::CaseInsensitive)) {
        cmd = QString("fpc -g %i");
    } else
        return;

    FileOptionDlg dlg(this);
    dlg.setCompileCmd(cmd);
    dlg.setCompileOption(m_options.m_compileOpt);
    dlg.setExecOption(m_options.m_execOpt);
    if (dlg.exec() == QDialog::Accepted) {
        m_options.m_compileOpt = dlg.getCompileOption();
        m_options.m_execOpt = dlg.getExecOption();
    }
}

QString MEditor::getCompileOption()
{
    return m_options.m_compileOpt;
}

QString MEditor::getExecOption()
{
    return m_options.m_execOpt;
}

void MEditor::setStepMarker (int line /* = -1 */)
{
    -- line;
    int stepLine = markerLine(m_stepHandle);
    //if (m_stepLine != line)
    if (stepLine != line)
    {
        // remove the current step marker if exists
        //if (m_stepLine >= 0)
        if (stepLine >= 0)
        {
            //markerDelete (m_stepLine, m_stepMarker);
            //m_stepLine = -1;
            markerDelete(stepLine, m_stepMarker);
        }

        if (line >= 0)
        {
            m_stepHandle = markerAdd (line, m_stepMarker);

            /*
                For BACKGROUND marker, the insertion and
                removal of marker will not be repainted
                until some other things happen which cause
                editor to repaint.

                Here we set cursor to the line of marker to
                cause editor to repaint.
            */
            setCursorPosition (line, 0);
            ensureLineVisible (line);
            setFocus ();
        }
    }
}

void MEditor::contextMenuEvent(QContextMenuEvent *event)
{
    long chpos = SendScintilla(SCI_POSITIONFROMPOINTCLOSE, event->x(), event->y());
    long start = SendScintilla(SCI_GETSELECTIONSTART);
    long end = SendScintilla(SCI_GETSELECTIONEND);
    if (selectedText().isEmpty() || chpos<start || chpos>end)
        SendScintilla(SCI_GOTOPOS, chpos);

    QMenu *menu = new QMenu(this);
    QFont font("Helvetica",9,QFont::Normal);
    menu->setFont(font);

    QAction* actionCopy = new QAction(tr("Copy"),this);
    QAction* actionCut = new QAction(tr("Cut"),this);
    QAction* actionPaste = new QAction(tr("Paste"),this);
    QAction* actionSelectAll = new QAction(tr("Select All"),this);
    QAction* actionAddWatch = 0;
    QAction* actionComment = new QAction(tr("Comment"), this);

    bool addWatchFlag = false;
    if ((curLine = selectedText()).length() == 0) {
        curLine = myGetWord(chpos);
        if (curLine.length() > 0) {
            addWatchFlag = true;
            if (curLine[0] != '_' && !curLine[0].isLetter()) {
                addWatchFlag = false;
            }
            for (int i = 1;i < curLine.length() && addWatchFlag;++i) {
                if (curLine[i] != '_' && !curLine[i].isDigit() && !curLine[i].isLetter()) {
                    addWatchFlag = false;
                    break;
                }
            }
        }
    } else {
        addWatchFlag = true;
        int start,end;
        for (start = 0;start < curLine.length();++start) {
            if (curLine[start] == '_' || curLine[start].isLetter() || curLine[start].isDigit()) {
                break;
            }
        }
        for (end = start;end < curLine.length();++end) {
            if (curLine[end] == '\n') {
                break;
            }
        }
        curLine = curLine.mid(start,end - start);
    }

    connect(actionComment, SIGNAL(triggered()), this, SLOT(slotComment()));
    connect(actionCopy, SIGNAL(triggered()), this, SLOT(copy()));
    actionCopy->setEnabled(m_copyAvailable);
    connect(actionCut,SIGNAL(triggered()),this,SLOT(cut()));
    actionCut->setEnabled(m_copyAvailable);
    connect(actionPaste,SIGNAL(triggered()),this,SLOT(paste()));
    actionPaste->setEnabled(m_pasteAvailable);
    connect(actionSelectAll,SIGNAL(triggered()),this,SLOT(selectAll()));

    QString str = tr("Add to watch: ");

    if (addWatchFlag) {
        actionAddWatch = new QAction(str + curLine, this);
        menu->addAction(actionAddWatch);
        connect(actionAddWatch,SIGNAL(triggered()), this, SLOT(slotAddVarWatch()));
    }

    menu->addAction(actionSelectAll);
    menu->addSeparator();
    menu->addAction(actionCopy);
    menu->addAction(actionCut);
    menu->addAction(actionPaste);
    menu->addSeparator();
    menu->addAction(actionComment);

    menu->exec(event->globalPos());
    delete menu;
    delete actionCopy;
    delete actionCut;
    delete actionPaste;
    delete actionSelectAll;

    if (addWatchFlag) {
        delete actionAddWatch;
    }
}

void MEditor::slotAddVarWatch()
{
    m_editorTab->slotAddVarWatch(curLine);
}

void MEditor::slotComment()
{
    if (hasSelectedText()) {
        int lnb, inb, lne, ine;
        getSelection(&lnb, &inb, &lne, &ine);
        QString lb = text(lnb);
        QString le = text(lne);
        bool cTypeComment = false;
        for (int i = inb-1; i >= 0; --i) {
            if (!lb[i].isSpace()) {
                cTypeComment = true;
                break;
            }
        }
        for (int i = ine; i < le.length() && !cTypeComment; ++i) {
            if (!le[i].isSpace()) {
                cTypeComment = true;
                break;
            }
        }
        if (cTypeComment) {
            SendScintilla(SCI_BEGINUNDOACTION);
            insertAt("/*", lnb, inb);
            if (lnb == lne)
                ine += 2;
            insertAt("*/", lne, ine);
            SendScintilla(SCI_ENDUNDOACTION);
        } else {
            SendScintilla(SCI_BEGINUNDOACTION);
            for (int i = lnb; i <= lne; i++) {
                insertAt("//", i, 0);
            }
            SendScintilla(SCI_ENDUNDOACTION);
        }
    }
}

char MEditor::myGetCharacter(long &pos)
{
    if (pos <= 0)
        return '\0';

    char ch = SendScintilla(SCI_GETCHARAT, --pos);

    // Don't go past the end of the previous line.
    if (ch == '\n' || ch == '\r')
    {
        ++pos;
        return '\0';
    }

    return ch;
}

QString MEditor::myGetWord(long &pos)
{
    QString word;
    bool numeric = true;
    char ch;

    while ((ch = SendScintilla(SCI_GETCHARAT, pos)) != '\0') {
        if (!isWordCharacter(ch)) {
            break;
        }
        ++pos;
    }

    while ((ch = myGetCharacter(pos)) != '\0')
    {
        if (!isWordCharacter(ch))
        {
            ++pos;
            break;
        }

        if (ch < '0' || ch > '9')
            numeric = false;

        word.prepend(ch);
    }

    // We don't auto-complete numbers.
    if (numeric)
        word.truncate(0);

    return word;
}

QString  MEditor::getCurLine()
{
    bool flag = false;
    curLine = "";
    if ((curLine = selectedText()).length() == 0) {
        //int chpos = SendScintilla(SCI_POSITIONFROMPOINTCLOSE, event->x(), event->y());
        long chpos = SendScintilla(SCI_GETCURRENTPOS);
        curLine = myGetWord(chpos);
        if (curLine.length() > 0) {
            flag = true;
            if (curLine[0] != '_' && !curLine[0].isLetter()) {
                flag = false;
            }

            for (int i = 1;i < curLine.length() && flag;++i) {
                if (curLine[i] != '_' && !curLine[i].isDigit() && !curLine[i].isLetter()) {
                    flag = false;
                    break;
                }
            }
        }
    } else {
        flag = true;
        int start,end;
        for (start = 0;start < curLine.length();++start) {
            if (curLine[start] == '_' || curLine[start].isLetter() || curLine[start].isDigit()) {
                break;
            }
        }
        for (end = start;end < curLine.length();++end) {
            if (curLine[end] == '\n') {
                break;
            }
        }
        curLine = curLine.mid(start,end - start);
    }
    if (!flag) curLine = "";

    return curLine;
}

void MEditor::clickToggleBreakpoint(int margin, int line, Qt::KeyboardModifiers)
{
    if (margin == SC_MARGIN_NUMBER) {
        emit bkptToggleByClicked(getFileName(), line + 1);
    }
}

void MEditor::showTooltip(QString function) {
    QDir dir;
    bool contain = false;
    QString parentDir;
    const QMap<QString, QStringList> *em = NULL;
    if (pasSrc) {
        dir = HelpWidget::getPasHelpPath();
        em = HelpWidget::getPasHelpNames();
    } else {
        dir = HelpWidget::getCHelpPath();
        em = HelpWidget::getCHelpNames();
    }
    if (em->value("man3p").contains(function, Qt::CaseInsensitive)) {
        parentDir = "man3p";
        contain = true;
    }
    for (QMap<QString, QStringList>::ConstIterator i = em->begin();
         (i != em->end()) && (contain == false); i++) {
        if (i.key() == "man3p")
            continue;
        if (i.value().contains(function, Qt::CaseInsensitive)) {
            parentDir = i.key();
            contain = true;
        }
    }
    if (contain) {
        helpHtml = dir.absolutePath() + '/' + parentDir + '/' + function + ".html";
        QFile file(helpHtml);
        qDebug() << helpHtml;

        if (!file.open(QIODevice::ReadOnly)) {
            setToolTip("open file error");
            return;
        }
        QTextStream in(&file);
        QString tmp = in.readLine();
        QString toShow;
        if (pasSrc) {
            while (!tmp.startsWith("<h1>", Qt::CaseInsensitive)) {
                tmp = in.readLine();
                if (tmp.isNull()) break;
            }
            toShow = tmp;
            tmp = in.readLine();
            int lineNumber = 0;
            while (!tmp.startsWith("<H2>DESCRIPTION</H2>", Qt::CaseInsensitive)) {
                toShow += tmp + "\n";
                tmp = in.readLine();
                if (tmp == "<P>") {
                    lineNumber++;
                }
                if (lineNumber > 2) {
                    toShow += "<P>... ... ...";
                    break;
                }
                if (tmp.isNull()) break;
            }
        } else {
            bool jump = false;
            while ( tmp != QString("<H2>SYNOPSIS</H2>") &&
                   !(jump = tmp.trimmed().startsWith("location.href=")) ) {
                tmp = in.readLine();
                if (tmp.isNull()) break;
            }
            if (jump) {
                QString newFuction = tmp.section('\"', 1, 1).section('/', -1).section('.', 0, 0);
                showTooltip(newFuction);
                return;
            }
            toShow = tmp;
            tmp = in.readLine();
            while (!tmp.startsWith("<H3>") && !tmp.startsWith("<H2>")) {
                toShow += tmp + "\n";
                tmp = in.readLine();
                if (tmp.isNull()) break;
            }
            while (tmp != QString("<H2>DESCRIPTION</H2>")) {
                tmp = in.readLine();
                if (tmp.isNull()) break;
            }
            toShow += tmp + "\n";
            tmp = in.readLine();
            int lineNumber = 0;
            while (!tmp.startsWith("<H3>") && !tmp.startsWith("<H2>")) {
                toShow += tmp + "\n";
                tmp = in.readLine();
                if (tmp == "<P>") {
                    lineNumber++;
                }
                if (lineNumber > 2) {
                    toShow += "<P>... ... ...";
                    break;
                }
                if (tmp.isNull()) break;
            }
        }
        setToolTip(toShow.trimmed());
    }
}
